package com.starsky.common.mongodb.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.ListCollectionsIterable;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.starsky.common.mongodb.entity.MongoDBGroupByParams;
import com.starsky.common.page.PageData;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * mongodb curd 工具类
 */
@Component
public class MongodbUtils {
    private static final int FIRST_PAGE_NUM = 1;
    private static final String ID = "_id";

    public static MongodbUtils mongodbUtils;
    @Autowired
    private MongoTemplate mongoTemplate;

    @PostConstruct
    public void init() {
        mongodbUtils = this;
        mongoTemplate = this.mongoTemplate;
    }


    /**
     * 查询所集合
     *
     * @return
     */
    public static List<Map<String, Object>> getCollections() {
        List<Map<String, Object>> resList = new LinkedList<>();
        MongoDatabase db = mongodbUtils.mongoTemplate.getDb();
        ListCollectionsIterable<Document> documents = db.listCollections();
        for (Document document : documents) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("name", document.get("name"));
            map.put("options", document.get("options"));
            map.put("info", document.get("info"));
            map.put("idIndex", document.get("idIndex"));
            resList.add(map);
        }
        return resList;
    }

    /**
     * 功能描述: 创建一个集合
     * 同一个集合中可以存入多个不同类型的对象，我们为了方便维护和提升性能，
     * 后续将限制一个集合中存入的对象类型，即一个集合只能存放一个类型的数据
     *
     * @param name 集合名称，相当于传统数据库的表名
     * @return:void
     */
    public static void createCollection(String name) {
        mongodbUtils.mongoTemplate.createCollection(name);
    }

    /**
     * 功能描述: 创建索引
     * 索引是顺序排列
     *
     * @param collectionName 集合名称，相当于关系型数据库中的表名
     * @param filedName      对象中的某个属性名
     * @param isunique       是否设置唯一索引
     * @return:java.lang.String
     */
    public static String createIndex(String collectionName, String filedName, boolean isunique) {
        //配置索引选项
        IndexOptions options = new IndexOptions();
        // 设置为唯一
        options.unique(isunique);
        //创建按filedName升序排的索引
        return mongodbUtils.mongoTemplate.getCollection(collectionName).createIndex(Indexes.ascending(filedName), options);
    }

    /**
     * 功能描述: 获取当前集合对应的所有索引的名称
     *
     * @param collectionName
     * @return:java.util.List<java.lang.String>
     */
    public static List<String> getAllIndexes(String collectionName) {
        ListIndexesIterable<Document> list = mongodbUtils.mongoTemplate.getCollection(collectionName).listIndexes();
        //上面的list不能直接获取size，因此初始化arrayList就不设置初始化大小了
        List<String> indexes = new ArrayList<>();
        for (Document document : list) {
            document.entrySet().forEach((key) -> {
                //提取出索引的名称
                if (key.getKey().equals("name")) {
                    indexes.add(key.getValue().toString());
                }
            });
        }
        return indexes;
    }

    /**
     * 保存数据对象，集合为数据对象中@Document 注解所配置的collection
     * 保存时，有则更新，无则插入
     *
     * @param obj 数据对象
     */
    public static Object save(Object obj) {
        Object result = mongodbUtils.mongoTemplate.save(obj);
        return result;
    }

    /**
     * 指定集合保存数据对象，保存时，有则更新，无则插入
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     */
    public static Object save(Object obj, String collectionName) {
        Object result = mongodbUtils.mongoTemplate.save(obj, collectionName);
        return result;
    }

    /**
     * 功能描述: 往对应的集合中批量插入数据，注意批量的数据中不要包含重复的id
     *
     * @param infos 对象列表
     * @return:void
     */
    public static Collection<Object> saveMulti(List<Object> infos, String collectionName) {
        Collection<Object> objects = mongodbUtils.mongoTemplate.insert(infos, collectionName);
        return objects;
    }

    /**
     * 新增数据
     *
     * @param obj：数据
     */
    public static Object insert(Object obj) {
        Object result = mongodbUtils.mongoTemplate.insert(obj);
        return result;
    }

    /**
     * 新增数据
     *
     * @param obj：s数据
     * @param collectionName： 集合名称
     */
    public static Object insert(Object obj, String collectionName) {
        Object result = mongodbUtils.mongoTemplate.insert(obj, collectionName);
        return result;
    }

    /**
     * 新增数据
     *
     * @param obj：s数据
     * @param collectionName： 集合名称
     */
    public static Collection<?> insert(Collection<?> obj, String collectionName) {
        Collection result = mongodbUtils.mongoTemplate.insert(obj, collectionName);
        return result;
    }

    /**
     * 根据数据对象中的id删除数据，集合为数据对象中@Document 注解所配置的collection
     *
     * @param obj 数据对象
     */
    public static long remove(Object obj) {
        DeleteResult result = mongodbUtils.mongoTemplate.remove(obj);
        long deletedCount = result.getDeletedCount();
        return deletedCount;
    }

    /**
     * 指定集合 根据数据对象中的id删除数据
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     */
    public static long remove(Object obj, String collectionName) {
        DeleteResult result = mongodbUtils.mongoTemplate.remove(obj, collectionName);
        long deletedCount = result.getDeletedCount();
        return deletedCount;
    }

    /**
     * 根据key，value到指定集合删除数据
     *
     * @param key            键
     * @param value          值
     * @param collectionName 集合名
     */
    public static long removeById(String key, Object value, String collectionName) {
        Criteria criteria = Criteria.where(key).is(value);
        criteria.and(key).is(value);
        Query query = Query.query(criteria);
        DeleteResult result = mongodbUtils.mongoTemplate.remove(query, collectionName);
        long deletedCount = result.getDeletedCount();
        return deletedCount;
    }


    /**
     * 根据key，value到指定集合删除数据
     *
     * @param query          ：查询条件
     * @param collectionName 集合名
     */
    public static long remove(Query query, String collectionName) {
        DeleteResult result = mongodbUtils.mongoTemplate.remove(query, collectionName);
        long deletedCount = result.getDeletedCount();
        return deletedCount;
    }

    /***
     * 删除对象
     * @param object
     * @return
     */
    public int delete(Object object) {
        return (int) this.mongoTemplate.remove(object).getDeletedCount();
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param query  修改条件
     * @param update 修改内容
     * @param clazz  集合名
     */
    public static long updateFirst(Query query, Update update, Class clazz) {
        UpdateResult result = mongodbUtils.mongoTemplate.updateFirst(query, update, clazz);
        long modifiedCount = result.getModifiedCount();
        return modifiedCount;
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param query          修改条件
     * @param updateObject   修改内容
     * @param collectionName 集合名
     */
    public static long updateFirst(Query query, Map<String, Object> updateObject, String collectionName) {
        Update update = new Update();
        for (String key : updateObject.keySet()) {
            update.set(key, updateObject.get(key));
        }
        UpdateResult result = mongodbUtils.mongoTemplate.updateFirst(query, update, collectionName);
        long modifiedCount = result.getModifiedCount();
        return modifiedCount;
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateObject   修改内容
     * @param collectionName 集合名
     */
    public static long updateFirst(String accordingKey, Object accordingValue, Map<String, Object> updateObject,
                                   String collectionName) {
        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        long modifiedCount = updateFirst(query, updateObject, collectionName);
        return modifiedCount;
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param param          修改条件  集合
     * @param objectMap      修改内容 集合
     * @param collectionName 集合名
     */
    public static void updateFirst(Map<String, Object> param, Map<String, Object> objectMap, String collectionName) {
        Query query = new Query();
        for (Map.Entry<String, Object> entry : param.entrySet()) {
            query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));
        }
        Update update = new Update();
        for (Map.Entry<String, Object> entry : objectMap.entrySet()) {
            update.set(entry.getKey(), entry.getValue());
        }
        mongodbUtils.mongoTemplate.updateFirst(query, update, collectionName);
    }

    /**
     * 指定集合 修改数据，且仅修改找到的所有数据
     *
     * @param query          修改条件
     * @param objectMap      修改内容 集合
     * @param collectionName 集合名
     */
    public static long updateMulti(Query query, Map<String, Object> objectMap, String collectionName) {
        Update update = new Update();
        for (Map.Entry<String, Object> entry : objectMap.entrySet()) {
            update.set(entry.getKey(), entry.getValue());
        }
        UpdateResult updateResult = mongodbUtils.mongoTemplate.updateMulti(query, update, collectionName);
        long modifiedCount = updateResult.getModifiedCount();
        return modifiedCount;
    }

    public static Criteria getCondtion(Map<String, Object> param) {
        Criteria criteria = new Criteria();
        if (param != null && param.size() > 0) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                criteria.where(entry.getKey()).is(entry.getValue());
            }
        }
        return criteria;
    }

    /**
     * 指定集合 修改数据，且仅修改找到的所有数据
     *
     * @param param          修改条件  集合
     * @param objectMap      修改内容 集合
     * @param collectionName 集合名
     */
    public static long updateMulti(Map<String, Object> param, Map<String, Object> objectMap, String collectionName) {
        Query query = new Query();
        if (param != null && param.size() > 0) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));
            }
        }
        long modifiedCount = updateMulti(query, objectMap, collectionName);
        return modifiedCount;
    }

    /**
     * 指定集合 修改数据，且修改所找到的所有数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateObject   修改内容
     * @param collectionName 集合名
     */
    public static long updateMulti(String accordingKey, Object accordingValue,
                                   Map<String, Object> updateObject,
                                   String collectionName) {

        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        long modifyCount = updateMulti(query, updateObject, collectionName);
        return modifyCount;
    }

    /**
     * 功能描述: 使用索引信息精确更改某条数据
     *
     * @param id             唯一键
     * @param collectionName 集合名称
     * @param info           待更新的内容
     * @return:void
     */
    public static void updateById(Object id, String collectionName, Object info) {
        Query query = new Query(Criteria.where("id").is(id));
        Update update = new Update();
        String str = JSON.toJSONString(info);
        JSONObject jQuery = JSON.parseObject(str);
        jQuery.forEach((key, value) -> {
            //因为id相当于传统数据库中的主键，这里使用时就不支持更新，所以需要剔除掉
            if (!key.equals("id")) {
                update.set(key, value);
            }
        });
        mongodbUtils.mongoTemplate.updateMulti(query, update, info.getClass(), collectionName);
    }

    /**
     * 更新数据，有则更新，无则插入
     *
     * @param query
     * @param updateObject
     * @param collectionName
     */
    public static long upsert(Query query, Map<String, Object> updateObject, String collectionName) {
        Update update = new Update();
        for (String key : updateObject.keySet()) {
            update.set(key, updateObject.get(key));
        }
        UpdateResult upsert = mongodbUtils.mongoTemplate.upsert(query, update, collectionName);
        long modifiedCount = upsert.getModifiedCount();
        return modifiedCount;
    }

    /***
     * 根据id从几何中查询对象
     * @param id
     * @return
     */
    public T findById(Object id, Class<T> obj) {
        Query query = new Query(Criteria.where("_id").is(id));
        return this.mongoTemplate.findById(query, obj);
    }

    /**
     * 根据条件查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
     *
     * @param query 查询条件
     * @param obj   数据对象
     * @return
     */
    public static List find(Query query, Class obj) {
        List list = mongodbUtils.mongoTemplate.find(query, obj);
        return list;
    }

    /**
     * 根据条件查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
     *
     * @param search 查询条件 key
     * @param obj    数据对象
     * @return
     */
    public static List find(Map<String, Object> search, Class obj) {
        Query query = new Query();
        query.addCriteria(getCondtion(search));
        List<T> resultList = mongodbUtils.mongoTemplate.find(query, obj);
        return resultList;
    }

    /**
     * 指定集合 根据条件查询出所有结果集
     *
     * @param search         查询条件 value
     * @param obj            数据对象
     * @param collectionName 集合名
     * @return
     */
    public static List find(Map<String, Object> search, Class obj, String collectionName) {
        Query query = new Query();
        if (search != null && search.size() > 0) {
            for (String key : search.keySet()) {
                query.addCriteria(Criteria.where(key).is(search.get(key)));
            }
        }
        List resultList = mongodbUtils.mongoTemplate.find(query, obj, collectionName);
        return resultList;
    }

    /**
     * 指定集合 根据条件查询出所有结果集 并排倒序
     *
     * @param obj            数据对象
     * @param search         查询条件 value
     * @param collectionName 集合名
     * @param sort           排序字段
     * @return
     */
    public static <T> List<T> find(Map<String, Object> search, String collectionName, String sort, Class obj) {

        Query query = new Query();
        if (search != null && search.size() > 0) {
            for (String key : search.keySet()) {
                query.addCriteria(Criteria.where(key).is(search.get(key)));
            }
        }
        query.with(Sort.by(Direction.DESC, sort));
        List resultList = mongodbUtils.mongoTemplate.find(query, obj, collectionName);
        return resultList;
    }

    /**
     * 根据条件查询出符合的第一条数据 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param query: 查询条件
     * @param obj    数据对象
     * @return
     */
    public static Object findOne(Query query, Class obj) {
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj);
        return resultObj;
    }

    /**
     * 根据条件查询出符合的第一条数据 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param obj    数据对象
     * @param search
     * @return
     */
    public static Object findOne(Map<String, Object> search, Class obj) {
        Query query = new Query();
        if (search != null && search.size() > 0) {
            for (String key : search.keySet()) {
                query.addCriteria(Criteria.where(key).is(search.get(key)));
            }
        }
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj);
        return resultObj;
    }

    /**
     * 根据条件查询出符合的第一条数据并排序 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param obj    数据对象
     * @param search
     * @return
     */
    public static Object findOne(Map<String, Object> search, String sortFeild, Direction direction, Class obj) {
        Query query = new Query();
        if (search != null && search.size() > 0) {
            for (String key : search.keySet()) {
                query.addCriteria(Criteria.where(key).is(search.get(key)));
            }
        }
        query.with(Sort.by(new Order(Direction.DESC, sortFeild)));
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj);
        return resultObj;
    }

    /**
     * 指定集合 根据条件查询出符合的第一条数据
     *
     * @param obj            数据对象
     * @param search         查询条件 value
     * @param collectionName 集合名
     * @return
     */
    public static Object findOne(Map<String, Object> search, Class obj, String collectionName) {

        Query query = new Query();
        if (search != null && search.size() > 0) {
            for (String key : search.keySet()) {
                query.addCriteria(Criteria.where(key).is(search.get(key)));
            }
        }
        Object resultObj = mongodbUtils.mongoTemplate.findOne(query, obj, collectionName);
        return resultObj;
    }

    /**
     * 根据条件查询集合
     *
     * @param object
     * @return
     */
    public static List queryList(Object object, Class obj) {
        Query query = getQueryByObject(object);
        return mongodbUtils.mongoTemplate.find(query, obj);
    }

    /***
     * 根据属性获取对象属性值
     * @param fieldName
     * @param o
     * @return
     */
    private static Object getFieldValueByName(String fieldName, Object o) {
        try {
            String e = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + e + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[0]);
            return method.invoke(o, new Object[0]);
        } catch (Exception var6) {
            return null;
        }
    }


    /**
     * 将查询条件对象转换为query
     *
     * @param object
     * @return
     * @author Jason
     */
    private static Query getQueryByObject(Object object) {
        Query query = new Query();
        String[] fileds = getFiledName(object);
        Criteria criteria = new Criteria();
        for (int i = 0; i < fileds.length; i++) {
            String filedName = (String) fileds[i];
            Object filedValue = getFieldValueByName(filedName, object);
            if (filedValue != null) {
                criteria.and(filedName).is(filedValue);
            }
        }
        query.addCriteria(criteria);
        return query;
    }

    /***
     * 获取对象属性返回字符串数组
     * @param o
     * @return
     */
    private static String[] getFiledName(Object o) {
        Field[] fields = o.getClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];

        for (int i = 0; i < fields.length; ++i) {
            fieldNames[i] = fields[i].getName();
        }

        return fieldNames;
    }

    /**
     * 功能描述: 根据id查询信息
     *
     * @param id             注解
     * @param clazz          类型
     * @param collectionName 集合名称
     * @return:java.util.List<T>
     */

    public static Object selectById(String id, Class<? extends Object> clazz, String collectionName) {
        // 查询对象的时候，不仅需要传入id这个唯一键，还需要传入对象的类型，以及集合的名称
        return mongodbUtils.mongoTemplate.findById(id, clazz, collectionName);
    }


    /**
     * 功能描述: 根据条件查询集合
     *
     * @param query 查询条件
     * @return
     */
    public List<T> selectByQuery(Query query, Class clazz) {
        List<T> objects = mongodbUtils.mongoTemplate.find(query, clazz);
        return objects;
    }

    /**
     * 功能描述: 根据条件查询集合
     *
     * @param collectName 集合名称
     * @param query       查询条件
     * @param clazz       对象类型
     * @return
     */
    public List<T> selectByQuery(String collectName, Query query, Class<T> clazz) {
        List<T> result = mongodbUtils.mongoTemplate.find(query, clazz, collectName);
        return result;
    }

    /**
     * 查询出所有结果集 集合为数据对象中 @Document 注解所配置的collection
     *
     * @param clazz 数据对象
     * @return
     */
    public static List findAll(Class clazz) {
        List resultList = mongodbUtils.mongoTemplate.findAll(clazz);
        return resultList;
    }


    /***
     * 根据条件分页查询
     * @param object
     * @param start 查询起始值
     * @param size  查询大小
     * @return
     */
    public List<T> getPage(T object, int start, int size, Class obj) {
        Query query = getQueryByObject(object);
        query.skip(start);
        query.limit(size);
        return this.mongodbUtils.mongoTemplate.find(query, obj);
    }

    /***
     * 根据条件查询库中符合条件的记录数量
     * @param object
     * @return
     */
    public Long getCount(T object, Class obj) {
        Query query = getQueryByObject(object);
        return this.mongodbUtils.mongoTemplate.count(query, obj);
    }

    /***
     * 根据条件查询库中符合条件的记录数量
     * @param query： 查询条件
     * @return
     */
    public Long getCount(Query query, Class obj) {
        return this.mongodbUtils.mongoTemplate.count(query, obj);
    }


    /**
     * 指定集合 查询出所有结果集
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     * @return
     */
    public static List<? extends Object> findAll(Object obj, String collectionName) {

        List<? extends Object> resultList = mongodbUtils.mongoTemplate.findAll(obj.getClass(), collectionName);
        return resultList;
    }

    /**
     * 模糊搜索
     *
     * @param searchkey
     * @param fieldName
     * @param clazz
     * @return
     */
    public static List<? extends Object> likeList(String searchkey, String fieldName, Class<? extends
            Object> clazz) {
        Pattern pattern = Pattern.compile("^.*" + searchkey + ".*$", Pattern.CASE_INSENSITIVE);
        Query query = new Query(Criteria.where(fieldName).regex(pattern));
        List<? extends Object> list = mongodbUtils.mongoTemplate.find(query, clazz);
        return list;
    }

    /**
     * 根据条件查询sum结果
     *
     * @param param
     * @param field
     * @param collectionName
     * @param Clazz
     * @return
     */
    public static List<? extends Object> sumFieldByParam(Map<String, Object> param,
                                                         String field, String collectionName,
                                                         Class Clazz) {
        Criteria criteria = new Criteria();
        if (param != null && param.size() > 0) {
            if (param != null) {
                for (String key : param.keySet()) {
                    criteria.and(key).is(param.get(key));
                }
            }
        }
        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria), Aggregation.group().sum(field).as(field));
        AggregationResults results = mongodbUtils.mongoTemplate.aggregate(aggregation, collectionName, Clazz);
        List<? extends Object> list = results.getMappedResults();
        return list;
    }

    /**
     * 根据条件查询count结果
     *
     * @param param
     * @param field
     * @param collectionName
     * @param Clazz
     * @return
     */
    public static List<? extends Object> countFieldByParam(Map<String, Object> param,
                                                           String groupName, String field,
                                                           String collectionName, Class Clazz) {
        Criteria criteria = new Criteria();
        if (param != null && param.size() > 0) {
            for (String key : param.keySet()) {
                criteria.and(key).is(param.get(key));
            }
        }
        Aggregation aggregation = null;
        if (StringUtils.isNotEmpty(groupName)) {
            aggregation = Aggregation.newAggregation(Aggregation.match(criteria), Aggregation.group(groupName).count().as(field));
        } else {
            aggregation = Aggregation.newAggregation(Aggregation.match(criteria), Aggregation.group().count().as(field));
        }
        AggregationResults results = mongodbUtils.mongoTemplate.aggregate(aggregation, collectionName, Clazz);
        List<? extends Object> list = results.getMappedResults();
        return list;
    }

    public static List countFieldByParam(Criteria criteria, String search,
                                         String groupName, String field,
                                         String collectionName, Class Clazz) {
        Aggregation aggregation = null;
        if (StringUtils.isNotEmpty(groupName)) {
            GroupOperation add = Aggregation.group(groupName).count().as(field);
            if (StringUtils.isNotEmpty(search)) {
                add = Aggregation.group(groupName).first(search).as(search).count().as(field);
            }
            aggregation = Aggregation.newAggregation(Aggregation.match(criteria), add);
        } else {
            aggregation = Aggregation.newAggregation(Aggregation.match(criteria), Aggregation.group().count().as(field));
        }
        AggregationResults results = mongodbUtils.mongoTemplate.aggregate(aggregation, collectionName, Clazz);
        List list = results.getMappedResults();
        return list;
    }

    /**
     * 分组查询
     *
     * @param params
     * @param Clazz
     * @return
     */
    public static List searchGroupList(MongoDBGroupByParams params, Class Clazz) {
        Aggregation aggregation = null;
        GroupOperation add = Aggregation.group(params.getGroupName());
        for (String param : params.getSearch()) {
            add = add.first(param).as(param);
        }
        SortOperation sort = Aggregation.sort(params.getSortDes(), params.getSort());
        aggregation = Aggregation.newAggregation(Aggregation.match(params.getCriteria()), add, sort);
        AggregationResults results = mongodbUtils.mongoTemplate.aggregate(aggregation, params.getCollectionName(), Clazz);
        List list = results.getMappedResults();
        return list;
    }


    /**
     * 根据条件获取总条数
     *
     * @param query
     * @param collectionName
     * @return
     */
    public static Long countFieldByParam(Query query, String collectionName) {
        Long data = mongodbUtils.mongoTemplate.count(query, collectionName);
        return data;
    }

    /**
     * 分组统计
     *
     * @param aggregation
     * @param input
     * @param output
     * @return
     */
    public static AggregationResults aggregate(Aggregation aggregation, Class input, Class output) {
        AggregationResults aggregate = mongodbUtils.mongoTemplate.aggregate(aggregation, input, output);
        return aggregate;
    }


    /**
     * 功能描述: 分页查询列表信息
     *
     * @param collectName 集合名称
     * @param clazz       返回实体类对象类型
     * @param currentPage 当前页码
     * @param pageSize    分页大小
     * @return:java.util.List<T>
     */
    public static PageData<T> selectList(String collectName, Class<T> clazz, Integer currentPage, Integer pageSize) {
        //设置分页参数
        Query query = new Query();
        //设置分页信息
        if (!ObjectUtils.isEmpty(currentPage) && ObjectUtils.isEmpty(pageSize)) {
            query.skip((currentPage - 1) * pageSize).limit(pageSize);
        }
        // 查询记录总数
        int totalCount = (int) mongodbUtils.mongoTemplate.count(query, clazz);
        //总记录
        List<T> list = mongodbUtils.mongoTemplate.find(query, clazz, collectName);

        return new PageData<>(list, totalCount);
    }


    /**
     * 功能描述: 根据条件查询集合
     *
     * @param collectName 集合名称
     * @param conditions  查询条件，目前查询条件处理的比较简单，仅仅做了相等匹配，没有做模糊查询等复杂匹配
     * @param clazz       返回实体类对象类型
     * @param currentPage 当前页码
     * @param pageSize    分页大小
     * @return:java.util.List<T>
     */

    public static PageData<T> selectByPage(String collectName, Map<String, String> conditions, Class<T> clazz, Integer currentPage, Integer pageSize) {
        if (ObjectUtils.isEmpty(conditions)) {
            return selectList(collectName, clazz, currentPage, pageSize);
        } else {
            //设置分页参数
            Query query = new Query();
            query.skip(currentPage).limit(pageSize);
            // 往query中注入查询条件
            conditions.forEach((key, value) -> query.addCriteria(Criteria.where(key).is(value)));
            // 查询记录总数
            int totalCount = (int) mongodbUtils.mongoTemplate.count(query, clazz);
//            总记录
            List<T> list = mongodbUtils.mongoTemplate.find(query, clazz, collectName);

            return new PageData(list, totalCount);
        }
    }

    /**
     * 功能描述: 根据条件分页查询集合
     *
     * @param collectName 集合名称
     * @param query       查询条件
     * @param clazz       返回实体类对象类型
     * @return
     */
    public static PageData<T> selectByPage(String collectName, Query query, Class<T> clazz) {
        // 查询记录总数
        int totalCount = (int) mongodbUtils.mongoTemplate.count(query, clazz);
//            总记录
        List<T> list = mongodbUtils.mongoTemplate.find(query, clazz, collectName);

        return new PageData(list, totalCount);
    }

    /**
     * 分页查询，直接返回集合类型的结果.
     */
    public <T> PageResult<T> pageQuery(Query query, Class<T> entityClass, Integer pageSize,
                                       Integer pageNum) {
        return pageQuery(query, entityClass, pageSize, pageNum, Function.identity(), null);
    }

    /**
     * 分页查询，直接返回集合类型的结果.
     */
    public <T> PageResult<T> pageQuery(Query query, Class<T> entityClass, Integer pageSize,
                                       Integer pageNum, String sortField, Sort.Direction order) {
        return pageQueryBySortField(query, entityClass, pageSize, pageNum, Function.identity(), sortField, order);
    }

    /**
     * 分页查询，不考虑条件分页，直接使用skip-limit来分页.
     */
    public <T, R> PageResult<R> pageQuery(Query query, Class<T> entityClass,
                                          Integer pageSize, Integer pageNum, Function<T, R> mapper) {
        return pageQuery(query, entityClass, pageSize, pageNum, mapper, null);
    }

    /**
     * 分页查询.
     *
     * @param query       Mongo Query对象，构造你自己的查询条件.
     * @param entityClass Mongo collection定义的entity class，用来确定查询哪个集合.
     * @param mapper      映射器，你从db查出来的list的元素类型是entityClass, 如果你想要转换成另一个对象，比如去掉敏感字段等，可以使用mapper来决定如何转换.
     * @param pageSize    分页的大小.
     * @param pageNum     当前页.
     * @param lastId      条件分页参数, 区别于skip-limit，采用find(_id>lastId).limit分页.
     *                    如果不跳页，像朋友圈，微博这样下拉刷新的分页需求，需要传递上一页的最后一条记录的ObjectId。 如果是null，则返回pageNum那一页.
     * @param <T>         collection定义的class类型.
     * @param <R>         最终返回时，展现给页面时的一条记录的类型。
     * @return PageResult，一个封装page信息的对象.
     */
    public static <T, R> PageResult<R> pageQuery(Query query, Class<T> entityClass,
                                                 Integer pageSize, Integer pageNum, Function<T, R> mapper, String lastId) {
        //分页逻辑
        long total = mongodbUtils.mongoTemplate.count(query, entityClass);
        final Integer pages = (int) Math.ceil(total / (double) pageSize);
        if (pageNum <= 0 || pageNum > pages) {
            pageNum = FIRST_PAGE_NUM;
        }
        final Criteria criteria = new Criteria();
        if (StringUtils.isNotBlank(lastId)) {
            if (pageNum != FIRST_PAGE_NUM) {
                criteria.and(ID).gt(new ObjectId(lastId));
            }
            query.limit(pageSize);
        } else {
            int skip = pageSize * (pageNum - 1);
            query.skip(skip).limit(pageSize);
        }

        final List<T> entityList = mongodbUtils.mongoTemplate
                .find(query.addCriteria(criteria)
                                .with(Sort.by(Sort.Direction.ASC, ID)),
                        entityClass);

        final PageResult<R> pageResult = new PageResult<>();
        pageResult.setTotal(total);
        pageResult.setPages(pages);
        pageResult.setPageSize(pageSize);
        pageResult.setPageNum(pageNum);
        if (mapper != null) {
            pageResult.setList(entityList.stream().map(mapper).collect(Collectors.toList()));
        } else {
            pageResult.setList((List<R>) entityList);
        }
        return pageResult;
    }


    /**
     * 分页查询.
     *
     * @param query       Mongo Query对象，构造你自己的查询条件.
     * @param entityClass Mongo collection定义的entity class，用来确定查询哪个集合.
     * @param mapper      映射器，你从db查出来的list的元素类型是entityClass, 如果你想要转换成另一个对象，比如去掉敏感字段等，可以使用mapper来决定如何转换.
     * @param pageSize    分页的大小.
     * @param pageNum     当前页.
     *                    如果不跳页，像朋友圈，微博这样下拉刷新的分页需求，需要传递上一页的最后一条记录的ObjectId。 如果是null，则返回pageNum那一页.
     * @param <T>         collection定义的class类型.
     * @param <R>         最终返回时，展现给页面时的一条记录的类型。
     * @return PageResult，一个封装page信息的对象.
     */
    public static <T, R> PageResult<R> pageQueryBySortField(Query query, Class<T> entityClass,
                                                            Integer pageSize, Integer pageNum, Function<T, R> mapper, String sortField, Sort.Direction order) {
        //分页逻辑
        long total = mongodbUtils.mongoTemplate.count(query, entityClass);
        final Integer pages = (int) Math.ceil(total / (double) pageSize);
        if (pageNum <= 0 || pageNum > pages) {
            pageNum = FIRST_PAGE_NUM;
        }
        int skip = pageSize * (pageNum - 1);
        query.skip(skip).limit(pageSize);
        if (StringUtils.isNotEmpty(sortField) && order != null) {
            query.with(Sort.by(order, sortField));
        }
        final List<T> entityList = mongodbUtils.mongoTemplate
                .find(query.with(Sort.by(order, sortField)),
                        entityClass);
        final PageResult<R> pageResult = new PageResult<>();
        pageResult.setTotal(total);
        pageResult.setPages(pages);
        pageResult.setPageSize(pageSize);
        pageResult.setPageNum(pageNum);
        if (mapper != null) {
            pageResult.setList(entityList.stream().map(mapper).collect(Collectors.toList()));
        } else {
            pageResult.setList((List<R>) entityList);
        }
        return pageResult;
    }

    /**
     * 分组查询
     *
     * @param params
     * @param Clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> pageGroupQueryBySortField(MongoDBGroupByParams params, Class<T> Clazz) {
        Aggregation aggregation;
        GroupOperation add = Aggregation.group(params.getGroupName());
        for (String param : params.getSearch()) {
            add = add.first(param).as(param);
        }
        SortOperation sort = Aggregation.sort(params.getSortDes(), params.getSort());
        SkipOperation skip = Aggregation.skip(params.getPageNo());
        LimitOperation limit = Aggregation.limit(params.getPagteSize());
        aggregation = Aggregation.newAggregation(Aggregation.match(params.getCriteria()), add, sort, skip, limit);
        AggregationResults results = mongodbUtils.mongoTemplate.aggregate(aggregation, params.getCollectionName(), Clazz);
        List<T> list = results.getMappedResults();
        return list;
    }

}
